# -*- encoding: utf-8 -*-

# Copyright 2010 István Szentandrási
# <etude-music-player@googlegroups.com>

# This file is part of Etude.
#
# Etude is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as 
# published by the Free Software Foundation, either version 3 
# of the License, or (at your option) any later version.
#
# Etude is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Etude. If not, see <http://www.gnu.org/licenses/>.

import ConfigParser as cp
import os
import main
import gobject
import client
import cgi
import re
import time
import datetime

from types import StringTypes

try:
    _
except:
    _ = lambda x: x

class Config(object):

    """
    Edits/Loads the saved configurations
    """

    def __init__(self, control):
        object.__init__(self)
        self.control = control
        self.load_config()


    def load_config(self):
        """
        load the default configuration file
        """
        self.cfg = cp.ConfigParser()
        self.cfg.read(os.path.join(main.CONFIG_DIR, 'config.cfg'))

    def update_defaults(self, section, v_dict, exit_on_section = False):
        """
        Update to the section a dictionary of key - values
        for example see plugins.py
        """
        if not self.cfg.has_section(section):
            self.cfg.add_section(section)
        elif exit_on_section: #if the section already exists return
            return
        for option, value in v_dict.items():
            if not self.cfg.has_option(section, option):
                self.cfg.set(section, option, value)
        self.save_config()

    def get(self, section, option):
        """
        get a configuration option from section 'section'
        """
        return self.cfg.get(section, option)

    def has(self, section, option = None):
        """
        returns if the given section exists. If option is set
        function returns if the given option exists in the section
        """
        if option == None:
            return self.cfg.has_section(section)
        else:
            return self.cfg.has_option(section, option)

    def set(self, section, option, value):
        """
        set a configuration option. If the section doesn't exists, it'll be created
        """
        if not self.cfg.has_section(section):
            self.cfg.add_section(section)
        self.cfg.set(section, option, value)

    def remove(self, section, option = None):
        """
        remove an option form section 'section', or remove a whole section
        if option is not set
        """
        if option == None:
            self.cfg.remove_section(section)
        else:
            self.cfg.remove_option(section, option)

    def section_items(self, section):
        """
        returns a list of options in a section
        """
        return self.cfg.items(section)

    def save_config(self):
        """
        saves the default configuration file
        """
        try:
            cfgfile = open( os.path.join(main.CONFIG_DIR, 'config.cfg'), 'wb')
        except IOError:
            #error opening config
            pass
        else:
            self.cfg.write(cfgfile)
            cfgfile.close()
    
def parse_column_types(pattern):
    """
    Parses the type of columns for a treemodel for a given pattern

    what is not found would miss 
    {} parentheses of fields from what show same of them, or if none is set, show nothing
     no sublevels
    | is a separator, function returns a list
    <artist> - artist
    <album> - album
    <smart> - title or filename instead if not found - if possible use other, no ordering
    <title>- title
    <tracknr> - tracknumber
    <url>- path - if file
    <bitrate> - bitrate
    <filename> - filename - if file - if possible use other - ordering won't work
    <duration> - song length
    <date> - date
    <genre> - genre
    <comment> - comment

    <id> - medialib id
    <laststarted> - Unix last started
    <added> - added to medialib
    <timesplayed> - times played
    """
    def filter(patt):
        if patt in ('<id>', '<timesplayed>', '<tracknr>', '<bitrate>'):
            return gobject.TYPE_INT
        else:
            return gobject.TYPE_STRING
    cols = [col.strip() for col in str(pattern).split('|')]
    return [filter(col) for col in cols]


RE_REPL_BW_PROPS = re.compile(r'>.*?<', re.DOTALL)

def parse_column_xmms(patt):
    """
    return a list of properties. 
    Useful for setting field argument for coll_query_infos

    The smart, filename properties- use only when you wan't to get an 
    order of fields to use with collections otherwise don't
    """
    if ('<smart>' in patt) or ('<filename>' in patt):
        patt = patt.replace('<smart>', '<title><channel><url>')
        patt = patt.replace('<filename>', '<url>')
    try:
        ind = patt.index('<')
    except ValueError:
        ind = 0
    try:
        endind = patt.rindex('>')
    except ValueError:
        endind = len(patt)
    patt = patt[ind+1:endind]
    ret_pat = RE_REPL_BW_PROPS.sub(' ', patt)
    return ret_pat.split(' ')

def parse_column_headers(pattern, capitalize = False):
    """
    Parse the column header names for a treemodel

    for pattern names see client.XMEP* and parse_column_types
    pleasu use only the ones which are available in xmms directly - (not smart,filename)
    """
    ret_pat = str(pattern)
    if capitalize:
        func = lambda x: _(x).capitalize()
    else:
        func = lambda x: _(x)
    for prop in [getattr(client, p) for p in dir(client) if p.startswith('XMEP_')]:
        ret_pat = ret_pat.replace('<'+prop+'>', func(prop))
    return [col.strip() for col in ret_pat.split('|')]

def parse_formatting_return_substring(format):
    """Split format along the { and } characters.
    
    For example: <artist>{-<title>} {<album>} -> ['<artist>', '{-<title>}',' ', '{<album>}']"""
    substrings = []
    end = format
    while len(end) > 0:
        begin, sep1, end = end.partition('{')
        if len(begin)>0:
            substrings.append(begin)
        if len(end) == 0:
            if sep1 != '':
                substrings.append(sep1)
            break
        begin, sep2, end = end.partition('}')
        substrings.append(sep1 + begin + sep2)
    return substrings


def format_value(prop, value):
    """
    sometimes the value needs special formatting before showing it
    """
    if prop == 'url' or prop == 'filename':
        return cgi.escape(client.decode_url(value))
    elif prop in ('added', 'laststarted'):
        try:
            if value == 0:
                raise TypeError
            #return time.strftime("%c", time.localtime(int(value)))
            now = datetime.datetime.now()
            return now.replace(microsecond = 0).isoformat(' ')
        except TypeError:
            return ''
    elif prop == 'bitrate':
        try:
            return int(value)/1000
        except TypeError:
            return 0
    elif prop in ('id', 'timesplayed', 'tracknr', 'status'):
        try:
            return int(value)
        except TypeError:
            return 0
    elif prop == 'duration':
        try:
            return client.get_interval(int(value)/1000)
        except TypeError:
            return '--:--'
    elif isinstance(value, StringTypes):
        return cgi.escape(value)
    else:
        return value

def parse_part(part, dict):
    """
    parse between {} and between }{
    """
    if part.startswith('{') and part.endswith('}'):
        brackets = True
    else:
        brackets = False
    try:
        ind = part.index('<')
        endind = part.rindex('>')
    except ValueError:
        return part
    from_replace = [prop for prop in re.sub(r'>.*?<', ' ', part[ind+1:endind]).split(' ')]
    ret_part = part
    
    #if smart or is in the pattern, we should create it and add it
    # to the dictionary
    if (('smart' in from_replace) or ('filename' in from_replace)) and ('filename' not in dict):
        try:
            if dict['url'].startswith('file:///'):
                begin,sep,end = dict['url'].rpartition('/')
                dict['filename'] = end
            else:
                dict['filename'] = dict['url']
        except KeyError:
            dict['filename'] = ''
    if ('smart' in from_replace) and ('smart' not in dict):
        if ('title' in dict) and (dict['title'] != '') and (dict['title'] != None):
            dict['smart'] = dict['title']
        elif ('channel' in dict) and (dict['channel'] != '') and (dict['channel'] != None):
            dict['smart'] = dict['channel']
        else:
            dict['smart'] = client.decode_url(dict['filename'])
    
    #replace properties
    for prop in from_replace:
        if (prop in dict) and (dict[prop] != '') and (dict[prop] != None):
            ret_part = ret_part.replace('<'+prop+'>', str(format_value(prop,dict[prop])))
        elif brackets:
            ret_part = ret_part.replace('<'+prop+'>', '')
        else:
            ret_part = ret_part.replace('<'+prop+'>', '#'+_("Unknown"))
    if (ret_part == re.sub(r'<.*?>', '', part)) and brackets:
        #if the part was in brackets and None of the properties were set
        #we should return an empty string
        return ''
    if brackets:
        ret_part = ret_part[1:-1]
    return ret_part


def parse_row(pattern, dict):
    """
    returns a row according to a pattern

    for one value on a row:
    if you a simple row, with columns it's simpler and faster to use list generation
    to get a row, for example: [cfg.format_value(key, result[key]) for key in row_struct] 
    you can get row_struct with parse_columns_xmms. 
        If you use in this case filename or smart, you should add them to the result dictionary
    """
    def deescape(pattern):
        return pattern.replace('\\n', '\n').replace('&', '&amp;').replace('[', '%60').replace(']', '%62')
    def deescape_markup(pattern):
        return pattern.replace('%60', '<').replace('%62', '>')
    pattern = deescape(pattern)
    lst = [pat.strip() for pat in pattern.split('|')]
    ret_lst = []
    for column in lst:
        substrs = parse_formatting_return_substring(column)
        if column in ('<id>', '<bitrate>', '<tracknr>', '<timesplayed>'):
            try:
                ret_lst.append(int(parse_part(substrs[0], dict)))
            except:
                ret_lst.append(0)
        else:
            ret_lst.append(''.join([parse_part(st, dict) for st in substrs]))
    return [deescape_markup(patt) for patt in ret_lst]

def list_unique(seq): # Dave Kirby
    # Order preserving
    seen = set()
    return [x for x in seq if x not in seen and not seen.add(x)]

